/*
    YmsgIRC - Program enabling YMSG chat clients to use IRC
    Copyright (C) 2011 Crash Daemonicus ( crashenator -at- gmail.com )

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common.h"
#include "YMSG_constants.h"
#include "YMSG_field.h"


/*
+ Description:
	An "Item" in the YMSG protocol is an object with properties in the form of fields.
	Their closest analog is a struct, but it is difficult to implement them as such.
	Items, like structs, generally have members in order/definition distinct to their type. However, in this case they are distinguished by an ID/Type number rather than type-name.
	The Item Type-number is assumed to be nonzero based on lack zero in observation. (this IS an undocumented protocol)

	When represented raw, Items appear to be constructed of fields, however they are marked with special fields (ITEMBEGIN, ITEMEND) which contain their Type-number as a value.
	These fields mark the beginning and ending of an item, fields that are between the markers are contained in it.

	Items most oftenly occur inside a List, which is like an array/vector of objects.

	However, there are a few rare exceptions to the above: Items can occur with multiple fields of the same type, Items can occur outside of Lists, etc.

+ About this implementation:
	To implement Items fully, if a bit clunky, this class defines an Item as an ItemType-number and an array/vector of fields.
	The Item Type number is by default set to 0 when not supplied, which is considered to be an invalid value. 
	This class supports conversion of the object to a string in YMSG format and to a vector of fields. This will include begin/end marker-fields if the ItemType is nonzero.

	You can use the condition of type==0 to specifically force string conversion to omit container/marker fields; 
	resulting in the string-conversion of only the fields vector.

	From the perspective of being a array of fields, this class allows searching the internal field vector in several ways including acquiring the value of a matching field. (read: getting the value of a property)
	This class also supports setting field values and appending new fields directly without requiring pushing YmsgField initializations to the vector.
*/
class YmsgItem{
	public:
		int type;// the identifier for the type of Item
		vector<YmsgField> fields;//fields contained by this Item
		YmsgItem();
		YmsgItem(int itype,YmsgField& field);
		YmsgItem(int itype,vector<YmsgField> fields2);

		vector<YmsgField>::const_iterator  find(const int& type,const string& data) const;//finds a matching field entry contained by this item
		vector<YmsgField>::const_iterator  find(const YmsgField& field) const;
		vector<YmsgField>::const_iterator find(int type, int occur=1) const;
		vector<YmsgField>::iterator findModifiable(int type,int occur=1);//duplicate of above method, just without a const iterator.

		string getValue(int type, int occur=1) const;//gets the value of a YmsgField with a matching FieldType
		void setValue(int num, const string& value);//sets the value of a matching Field
		void addField(int num, const string& value);//adds a field directly to the internal vector


		vector<YmsgField> toFields() const;//convert this Item to a vector of fields format
		string toString();//convert the Item to a string of fields in YMSG format

		const YmsgItem& operator=(vector<YmsgField> fields2);//set the contained fields
		const YmsgItem& operator+=(const YmsgField& field);//append a field to the contained-field vector
		//  operator+=(vector<field>)  ?


		vector<YmsgItem> operator+(const YmsgItem& item2);// representation of multiple Items
		vector<YmsgField> operator+(vector<YmsgField> fields);//representation of the Item amongst other Fields.

		void debug() const;

};

/*
+ Description:
	Similar to YMSG Items, YMSG Lists take the form of fields contained by Beginning and End marker fields (of type LISTBEGIN,LISTEND) with the a ListType-Number as a value.
	However, different from YMSG Items,  Lists almost always appear as a container for several subsequent Items. The Type-number for a List always appears the same as the Items it contains.
	In this way, at least in observation, Lists could be thought of as a vector of Item-type objects, eg: vector<Item315> list.

	An exception to this common format is when the ListType is greater than 10000, where sometimes instead of containing same-type Items, they contain same-type fields.

+ About this implementation
	This class implements lists as a Type-number and a vector of Items
	Conversion to a vector of fields and to raw string format is implemented by a method recursively to allow easy preparation before sending data.

	Also implemented is the ability to append items directly to the internal vector of items by parameters rather than pushing-back a Item initialization.

	Note:
	The exception where a List contains fields of the same type-number is not directly implemented.
	Currently it is suggested to implement this through by creating a YmsgList of the the appropriate type which contains a single YmsgItem with a type of 0
	(excludes container fields during Item-conversion) which in turn contains the fields.
	When this is converted to fields or to string, it will be the fields contained by the container fields for YmsgList

	Processing of incoming (read: raw) data to objects does not handle this condition currently, and will produce an empty List.

*/
class YmsgList{
	public:
		int type;
		vector<YmsgItem> items;
		YmsgList();
		YmsgList(int itype);
		YmsgList(int itype,YmsgItem& item);
		YmsgList(int itype,vector<YmsgItem> items2);

		void addItem(const vector<YmsgField>& fields);//add an item (initialized to the ListType number) containing the fields to the Item vector 
		void addItem(const YmsgItem& item);//add an item to the item-vector
		
		YmsgItem toItem();//this is just used as a container for fields (ie: for toFields() )
		vector<YmsgField> toFields() const;//convert the List and, recursively, the contained Items to a vector of fields
		string toString();//convert the List and contents to fields represented as a raw string in YMSG format.


		const YmsgList& operator=(vector<YmsgItem> items);//set vector
		const YmsgList& operator+=(const YmsgItem& item);//append vector with new item
		//  operator+=(vector<item>)  ?

		vector<YmsgList>  operator+(const YmsgList& list);//  list + list         = vector<list>
		vector<YmsgField> operator+(const YmsgField& field);//  list + field        = vector<field>
		vector<YmsgField> operator+(vector<YmsgField> fields);//list + vector<field> = vector<field>

		void debug() const;
};

